package cn.s1995.sloth.bizs.aspect;

import cn.hutool.core.util.StrUtil;
import cn.s1995.sloth.bizs.annotation.RedisLock;
import cn.s1995.sloth.common.core.util.SpelUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * RedisLockAspect
 *
 * @Author Lex
 * @Description redis锁切面
 * @Date 2023/11/21 15:49
 */
@Slf4j
@Aspect
@Component
@Order(-1)
public class RedisLockAspect {
    @Resource
    private RedissonClient redissonClient;

    private static final String REDISSON_LOCK_PREFIX = "lock:";

    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        String spel = redisLock.key();
        String lockName = redisLock.name();
        String key = getRedisKey(joinPoint, lockName, spel);
        RLock rLock = redissonClient.getLock(key);
        log.info("rLock.getName() - " + rLock.getName());
        rLock.lock(redisLock.expire(), redisLock.timeUnit());

        Object result = null;
        try {
            // 执行方法
            result = joinPoint.proceed();

        } finally {
            log.info("RedisLock unlock - " + key);
            rLock.unlock();
        }
        return result;
    }

    /**
     * 将spel表达式转换为字符串
     *
     * @param joinPoint 切点
     * @return redisKey
     */
    private String getRedisKey(ProceedingJoinPoint joinPoint, String lockName, String spel) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Object target = joinPoint.getTarget();
        Object[] arguments = joinPoint.getArgs();
        return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + SpelUtil.parse(target, spel, targetMethod, arguments);
    }
}
